package com.hmdp.service.impl;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.entity.ShopType;
import com.hmdp.mapper.ShopTypeMapper;
import com.hmdp.service.IShopTypeService;
import com.hmdp.utils.RedisConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hmdp.utils.RedisConstants.CACHE_SHOPTYPE_KEY;
import static com.hmdp.utils.RedisConstants.CACHE_SHOPTYPE_TTL;

/**
 *  服务实现类
 *  给前端返回的是对象，给Redis添加的是String
 *
 *  list or string
 */

/*
MyBatis-Plus如何知道查询哪张表：1.@TableName 注解 2. 泛型参数<> 3. 根据方法名动态生成sql
    实体类与表的映射：通过 @TableName 注解或默认规则，MyBatis-Plus 知道每个实体类对应的数据库表。
    Mapper 接口：通过泛型 <ShopType>，MyBatis-Plus 知道你正在操作哪个实体类。
    动态 SQL 生成：在执行查询时，MyBatis-Plus 根据实体类的表名映射和查询条件动态生成 SQL 语句，并发送到数据库执行。
* */
@Service
@Slf4j
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    //方法一：使用String类型作为缓存
    /*@Override
    public Result queryTypeList() {
        // 1. 从Redis中查询店铺类型缓存
        String shopTypeJson = stringRedisTemplate.opsForValue().get(CACHE_SHOPTYPE_KEY);
        // 2. 判定是否存在
        if (StrUtil.isNotBlank(shopTypeJson)) {
            // 3. 命中，刷新缓存ttl，将JSON转为对象返回
            stringRedisTemplate.expire(CACHE_SHOPTYPE_KEY, CACHE_SHOPTYPE_TTL, TimeUnit.DAYS);
            log.debug("debug：shopType命中");
            return Result.ok(JSONUtil.toList(shopTypeJson, ShopType.class));
        }
        // 4. 不存在，查询数据库
        List<ShopType> typeList = query().orderByAsc("sort").list();
        // 5. 不存在，返回错误
        if (typeList == null) {
            return Result.fail("查询店铺类型失败");
        }
        // 6. 存在，写入Redis作为缓存，并加入ttl
//        log.debug("typeList = {}", typeList);
        stringRedisTemplate.opsForValue().set(CACHE_SHOPTYPE_KEY, JSONUtil.toJsonStr(typeList));
        stringRedisTemplate.expire(CACHE_SHOPTYPE_KEY, CACHE_SHOPTYPE_TTL, TimeUnit.DAYS);
        log.debug("debug：shopType未命中");
        //7.返回
        return Result.ok(typeList);
    }*/

    //方法二：使用List类型作为缓存
    @Override
    public Result queryTypeList() {
        //查询redis：range从0到-1，即取出所有元素
        List<String> type_list_str = stringRedisTemplate.opsForList()
                .range(CACHE_SHOPTYPE_KEY, 0, -1);
        //判定是否存在
        if (type_list_str != null && !type_list_str.isEmpty()) {
            //命中，刷新缓存ttl，转为对象直接返回
            stringRedisTemplate
                    .expire(CACHE_SHOPTYPE_KEY, CACHE_SHOPTYPE_TTL, TimeUnit.DAYS);
            log.debug("debug：shopType命中！");
            List<ShopType> typeList = type_list_str
                    .stream()
                    .map(jsonStr -> JSONUtil.toBean(jsonStr, ShopType.class))
                    .collect(Collectors.toList());
            return Result.ok(typeList);
        }
        //未命中 查询数据库
        List<ShopType> type_list = list();
        if (type_list == null) {
            return Result.fail("未查询到商铺列表");
        }
        //数据库查到，将信息缓存到redis
        //先把ShopType类型的list数据转换成json字符串类型的的list：因为是stringRedisTemplate
        //缓存到redis，设置ttl
        type_list_str = type_list.stream().map(JSONUtil::toJsonStr).collect(Collectors.toList());
        stringRedisTemplate.opsForList().
                rightPushAll(CACHE_SHOPTYPE_KEY, type_list_str);
        stringRedisTemplate.expire(CACHE_SHOPTYPE_KEY, CACHE_SHOPTYPE_TTL, TimeUnit.DAYS);
        log.debug("debug：shopType未命中");
        //返回数据库查询的ShopType类型的list数据，不要返回字符串类型的的list，不然前端接收到的数据格式不对，无法渲染显示
        return Result.ok(type_list);
    }
}
